/*
 * Copyright (c) 2018-2023 北京华恒盛世科技有限公司
 * QUICKPOOL is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
*/

#include "quickpool.h"
#include "api.h"

static char sstrSuspendMsg[MAXMESSAGELENGTH+1];
static char sstrIndexMsg[MAXMESSAGELENGTH+1];
static char *sstrPendMsg;

struct msgMap {
    int  number;
    const char *message;
};

static void get_userindex_message(unsigned int, int, struct resource_definition *);
static void get_resource_message(int, int, struct resource_definition *);
static char *get_message(struct msgMap *, int);

char *
get_suspend_reason(int reasons, int subreasons, struct resource_definition *restable)
{
    sstrSuspendMsg[0] = '\0';

    log_message(DEBUG2, PENDINGREASON, "%s: reasons=%x, subreasons=%d",
                __func__, reasons, subreasons);

    if (reasons & SUSPEND_REASON_USER_STOP) {
        snprintf(sstrSuspendMsg, MAXMESSAGELENGTH, "User stopped the job.");
    } else if (reasons & SUSPEND_REASON_PREEMPTED) {
        snprintf(sstrSuspendMsg, MAXMESSAGELENGTH,
                 "The job was preempted by antoher higher priority job.");
    } else if (reasons & SUSPEND_REASON_QUEUE_WINDOW) {
        snprintf(sstrSuspendMsg, MAXMESSAGELENGTH, "Run windows of the queue are closed.");
    } else if (reasons & SUSPEND_REASON_USER_RESUME) {
        snprintf(sstrSuspendMsg, MAXMESSAGELENGTH, "Resumed job need to be scheduled.");
    } else if (reasons & SUSPEND_REASON_STOPCOND) {
        snprintf(sstrSuspendMsg, MAXMESSAGELENGTH, "Node load meeted with the job stop condition.");
    } else if (reasons & SUSPEND_REASON_RESUMECOND) {
        snprintf(sstrSuspendMsg, MAXMESSAGELENGTH,
                 "Node load does not meet with the job resume condition.");
    } else if (reasons & SUSPEND_REASON_RESOURCE) {
        snprintf(sstrSuspendMsg, MAXMESSAGELENGTH,
                 "Node available resources are not enough for job's requirements.");
    } else if (reasons & SUSPEND_REASON_NODEUNAVAIL) {
        snprintf(sstrSuspendMsg, MAXMESSAGELENGTH, "Node is unavailable by now.");
    } else if (reasons & SUSPEND_REASON_LOADSTOP) {

        switch (subreasons) {
        case R1M:
            snprintf(sstrSuspendMsg, MAXMESSAGELENGTH,
                     "1-minute CPU run queue length (r1m) exceeded load stop threshold.");
            break;
        case R5M:
            snprintf(sstrSuspendMsg, MAXMESSAGELENGTH,
                     "5-minute CPU run queue length (r5m) exceeded load stop threshold.");
            break;
        case R15M:
            snprintf(sstrSuspendMsg, MAXMESSAGELENGTH,
                     "15-minute CPU run queue length (r15m) exceeded load stop threshold.");
            break;
        case UT:
            snprintf(sstrSuspendMsg, MAXMESSAGELENGTH,
                     "CPU utilization (ut) exceeded load stop threshold.");
            break;
        case IO:
            snprintf(sstrSuspendMsg, MAXMESSAGELENGTH,
                     "Disk IO rate (io) exceeded load stop threshold.");
            break;
        case PG:
            snprintf(sstrSuspendMsg, MAXMESSAGELENGTH,
                     "Paging rate (pg) exceeded load stop threshold.");
            break;
        case MEM:
            snprintf(sstrSuspendMsg, MAXMESSAGELENGTH,
                     "Free memory (mem) exceeded load stop threshold.");
            break;
        case SWP:
            snprintf(sstrSuspendMsg, MAXMESSAGELENGTH,
                     "Feee swap (swp) exceeded load stop threshold.");
            break;
        case TMP:
            snprintf(sstrSuspendMsg, MAXMESSAGELENGTH,
                     "Available /tmp space (tmp) exceeded load stop threshold.");
            break;
        default:
            get_userindex_message(0, subreasons, restable);
            strncpy(sstrSuspendMsg, sstrIndexMsg, MAXMESSAGELENGTH);
            break;
        }
    } else if (reasons & SUSPEND_REASON_RESLIMIT) {
        switch (subreasons) {
        case SUBREASON_RUNTIME:
            snprintf(sstrSuspendMsg, MAXMESSAGELENGTH, "Job reached its runtime limit.");
            break;
        case SUBREASON_DEADLINE:
            snprintf(sstrSuspendMsg, MAXMESSAGELENGTH, "Job reached its termination time.");
            break;
        case SUBREASON_PROCESS:
            snprintf(sstrSuspendMsg, MAXMESSAGELENGTH, "PROCESSLIMIT was reached;\n");
            break;
        case SUBREASON_CPUTIME:
            snprintf(sstrSuspendMsg, MAXMESSAGELENGTH, "CPULIMIT was reached;\n");
            break;
        case SUBREASON_MEMORY:
            snprintf(sstrSuspendMsg, MAXMESSAGELENGTH, "MEMLIMIT was reached;\n");
            break;
        }
    } else {
        snprintf(sstrSuspendMsg, MAXMESSAGELENGTH, "Unknown suspend reason: %d", reasons);
    }

    return sstrSuspendMsg;
} // end function get_suspend_reason

char *
get_pending_reason(int numreason, int *reasonids, struct QuickPoolJobReply *jobreply,
                   struct resource_definition *restable)
{
    static int *reasonTb;
    static int memSize;
    struct msgMap pendMsg[] = {
        /* Job Related Reasons (1 - 100) */
        { REASON_NOT_SCHEDULE,
          "Job has not been scheduled"},
        { REASON_BEGIN_TIME,
          "Job will be scheduled after its begin time"},
        { REASON_DEPENDENCY,
          "Job has dependency"},
        { REASON_DEPENDENCY_SATISFIED,
          "Job's dependency cannot be satisfied"},
        { REASON_DEPENDENCY_SYNTAX,
          "Job's dependency has syntax error"},
        { REASON_MIGRATING,
          "Job is migrated and waiting for being scheduled"},
        { REASON_PRESCRIPT,
          "Fail to excecute job pre script"},
        { REASON_SWITCHING,
          "Job is switched to another queue and waiting for being scheduled"},
        { REASON_MODIFIED,
          "Job is modified and waiting for being scheduled"},
        { REASON_REQUEUED,
          "Job is requeued and waiting for being scheduled"},
        { REASON_AUTO_REQUEUE,
          "Job is auto requeued by the quickpool cluster"},
        { REASON_SCHEDULE_POSTPONE,
          "Queue has parameter to delay new job scheduling"},
        { REASON_PREEMPTED_RESUME,
          "Preempted job is resuming"},
        { REASON_ARRAYJOB_LIMIT,
          "Started job slots in the job array exceed its running slot limit"},
        { REASON_JOBGROUP_LIMIT,
          "Started job slots in the job group exceed its running slot limit"},
        { REASON_JOB_PTILE,
          "Host has not enough slots for job's ptile requirement"},
        { REASON_REQUEUE_LIMIT,
          "Job's requeue times exceed its queue's requeue limit setting"},
        { REASON_SUBMIT_SELECT,
          "Node load cannot meet the job submission requirement"},
        { REASON_TERMINATE_TIME,
          "Job might not finish before its termination time"},
        { REASON_SPAN_REQUIREMENT,
          "Node does not have enough slot to meet the job's processor spanning requirement"},
        { REASON_ZOMBIE_JOB,
          "Job is a zombie job"},

        /* Queue Related Reasons (101 - 200) */
        { REASON_QUEUE_INACTIVE,
          "The queue is inactive"},
        { REASON_QUEUE_RUNWINDOW,
          "The queue is out of its running time window"},
        { REASON_QUEUE_PRESCRIPT,
          "Faile to execution queue execution script for the job"},
        { REASON_QUEUE_SLOT_LIMIT,
          "The queue's maximum slot cannot meet the job's processor requirement"},
        { REASON_QUEUE_FREE_SLOT,
          "The queue has not enough free slots for the job's processor requirement"},
        { REASON_QUEUE_USER_FREE_SLOT,
          "The queue has not enough free slots for the job's processor requirement of the user"},
        { REASON_QUEUE_PROCESSOR_LIMIT,
          "The queue's processor limit cannot meet the job's processor requirement"},
        { REASON_QUEUE_NODE_SLOT_LIMIT,
          "The queue's node slot limit cannot meet the job's processor requirement"},
        { REASON_QUEUE_PTILE,
          "Not enough hosts to meet the queue's spanning requirement"},
        { REASON_QUEUE_NODE_MEMBER,
          "Node is not a member of the queue node list"},
        { REASON_QUEUE_SELECT,
          "Node load cannot meet the queue's requirement"},
        { REASON_QUEUE_RUNWINDOW_CLOSE,
          "The queue's run window will be closed before job finish"},
        { REASON_QUEUE_SPAN_REQUIREMENT,
          "Node does not have enough slot to meet the queue's spanning requirement"},

        /* User Related Reasons (201 - 300) */
        { REASON_USER_STOP,
          "User stop the pending job"},
        { REASON_USER_RESUME,
          "User resume the job"},
        { REASON_USER_SLOT_LIMIT,
          "Job's processor requirement exceed the user's maximum slot limit"},
        { REASON_USER_FREE_SLOT,
          "The user has not enough free slots for the job's processor requirement"},
        { REASON_USERGROUP_SLOT_LIMIT,
          "Job's processor requirement exceed the user group's maximum slot limit"},
        { REASON_USERGROUP_FREE_SLOT,
          "The user group has not enough free slots for the job's processor requirement"},

        /* Node Related Reasons (301 - 400) */
        { REASON_NODE_UNAVAIL,
          "Node is unavailable by now"},
        { REASON_NODE_ADMIN_CLOSED,
          "Node was closed by the cluster administrator"},
        { REASON_NODE_DISPWINDOW,
          "The node is out of its dispatching time window"},
        { REASON_NODE_HAS_JOB,
          "Exclusive job cannot be dispatched to the node that has runnong job"},
        { REASON_NODE_EXCLUSIVE,
          "Node is used exclusively by another job"},
        { REASON_NODE_POWERED_OFF,
          "Node was powered off by administrator or had no job running on it for a while"},
        { REASON_NODE_TYPE,
          "Node type is not the same as the submission node type"},
        { REASON_NODE_GPU_NUM,
          "Node has not enough GPUs"},
        { REASON_NODE_GPU_READYNUM,
          "Node has not enough available GPU"},
        { REASON_NODE_GPU_EXCLUSIVE,
          "Node has not enough exclusive GPU"},
        { REASON_NODE_GPU_MEMORY,
          "Node has not enough GPU with required memory"},
        { REASON_NODE_GPU_MODEL,
          "Node has not enough GPU with required model"},
        { REASON_NODE_WITHOUT_GPU,
          "Node has no GPU equipped"},
        { REASON_NODE_INTERVAL,
          "The node had a job started just now"},
        { REASON_NODE_DATASET,
          "Node does not register to the dataset"},             
        { REASON_NODE_NUMABIND,
          "Node has not enough cores for job to do numa binding"},    
        { REASON_FIRST_NODE,
          "Fist node is not in job ready node list"},    
        { REASON_NODE_FREE_SLOT,
          "The node has not enough free slots for the job's processor requirement"},
        { REASON_NODE_SLOT_LIMIT,
          "Job's processor requirement exceed the node's maximum slot limit"},
        { REASON_NODE_USER_SLOT_LIMIT,
          "The node's user slot limit cannot meet the job's processor requirement"},
        { REASON_JM_FORK,
          "JM on the node failed to fork a child to execution the job"},
        { REASON_JM_USER_ACCOUNT,
          "JM on the node does not have the user account to execute the job"},
        { REASON_JM_SETUP_DIR,
          "JM on the node failed to setup job execution directory"},
        { REASON_JM_CREATE_JOBFILE,
          "JM on the node failed to create job file"},
        { REASON_JM_EXECSCRIPT,
          "JM on the node failed to execute job execution script"},
        { REASON_JM_CHKPNT_DIR,
          "JM on the node failed to setup job check point directory"},
        { REASON_JM_SYSCALL,
          "JM on the node failed to initialize job execution environment due to syscall error"},
        { REASON_JM_RESTART_JOBPID,
          "JM on the node failed to get the PID of the restarting job"},
        { REASON_JM_NO_MEM,
          "JM on the node failed to allocate memory to execute the job"},
        { REASON_JM_XDR_ERROR,
          "Encode or decode message wrong when passing information between JM and JS"},
        { REASON_JM_ARGUMENT,
          "JM on the node cannot parse job submission argument from JS"},

        /* Load Related Reasons (401 - 1000) */
        { REASON_NODE_LOAD,
          "Node load exceeds its scheduling threshold"},

        /* Resource Related Reasons (1001 - 5000) */
        { REASON_QUEUE_RUSAGE,
          "Node does not have enough available resources to meet the queue's requirement"},
        { REASON_SUBMIT_RUSAGE,
          "Node does not have enough available resources to meet the job submission requirement"},
        { 0, NULL}
    };

    if (0 == numreason || NULL == reasonids) {
        quickpool_errno = ERROR_ARGUMENT;
        return EMPTY_STRING;
    }

    if (memSize < numreason) {

        FREEUP(reasonTb);
        reasonTb = (int *)calloc(numreason, sizeof(int));
        if (NULL == reasonTb) {
            memSize = 0;
	    quickpool_errno = ERROR_MEMORY;
            return EMPTY_STRING;
        }
        memSize = numreason;
    }

    int i;
    for (i=0; i<numreason; i++) {
        reasonTb[i] = reasonids[i];
    }

    FREEUP(sstrPendMsg);

    char *strNodeNames = NULL;
    int size = 0;
    if (NULL != jobreply && 0 != jobreply->num_node && NULL != jobreply->node_names) {
      
        size = jobreply->num_node * (MAXHOSTNAMELEN + 2);

        strNodeNames = (char *)calloc(size, sizeof(char));
        sstrPendMsg = (char *)calloc(jobreply->num_node, MAXHOSTNAMELEN + MAXMESSAGELENGTH);
        if (NULL == strNodeNames || NULL == sstrPendMsg) {
            quickpool_errno = ERROR_MEMORY;
            return EMPTY_STRING;
        }
    } else {
        sstrPendMsg = (char *)calloc(MAXMESSAGELENGTH, sizeof(char));
        if (NULL == sstrPendMsg) {
            quickpool_errno = ERROR_MEMORY;
            return EMPTY_STRING;
        }
    }

    for (i=0; i<numreason; i++) {

        if (0 == reasonTb[i]) {
            continue;
        }

        int reason;
        GET_LOW(reason, reasonTb[i]);
        if (0 == reason) {
            continue;
        }

        int nodeIndex;
        GET_HIGH(nodeIndex, reasonTb[i]);

        if (0 < nodeIndex) {
            if (NULL != jobreply && 0 != jobreply->num_node && NULL != jobreply->node_names) {
                snprintf(strNodeNames, size, "%s", jobreply->node_names[nodeIndex-1]);
            }
        }

	/* Search for the rest of the reasonTb and see whether there
         * is a matching reason.  If so, we add the associated host name
         * to the string stored in node name list so that we can provide a
         * summary information instead of repeating the same reason for
         * each host. */
        int j;
        for (j=i+1; j<numreason; j++) {

            if (0 == reasonTb[j]) {
                continue;
            }

            int reasonJ;
            GET_LOW(reasonJ, reasonTb[j]);

            if (reasonJ != reason) {
                continue;
            }

            GET_HIGH(nodeIndex, reasonTb[j]);

            reasonTb[j] = 0;
            if (0 < nodeIndex) {
                if (NULL != jobreply && 0 != jobreply->num_node && NULL != jobreply->node_names) {
                    snprintf(strNodeNames+strlen(strNodeNames), size,
                             ", %s", jobreply->node_names[nodeIndex-1]);
                }
            }
        }

        char *sp;
        if (reason >= REASON_NODE_LOAD && reason < REASON_QUEUE_RUSAGE) {

            get_resource_message(reason - REASON_NODE_LOAD, REASON_NODE_LOAD, restable);
            sp = sstrIndexMsg;
        } else if (reason >= REASON_QUEUE_RUSAGE && reason < REASON_SUBMIT_RUSAGE) {

            get_resource_message(reason - REASON_QUEUE_RUSAGE, REASON_QUEUE_RUSAGE, restable);
            sp = sstrIndexMsg;
        } else if (reason >= REASON_SUBMIT_RUSAGE) {

            get_resource_message(reason - REASON_SUBMIT_RUSAGE, REASON_SUBMIT_RUSAGE, restable);
            sp = sstrIndexMsg;
        } else {
            sp = get_message(pendMsg, reason);
        }

        if (NULL != strNodeNames && 0 < strlen(strNodeNames)) {
            snprintf(sstrPendMsg+strlen(sstrPendMsg), MAXMESSAGELENGTH,
                     "%s: %s;\n", sp, strNodeNames);
        } else {
            snprintf(sstrPendMsg+strlen(sstrPendMsg), MAXMESSAGELENGTH, "%s.", sp);
        }
    }
    FREEUP(strNodeNames);

    return sstrPendMsg;
} // end function get_pending_reason

/* ------------------------------------------------
 *
 *  below are static functions used in this file
 *
 * ------------------------------------------------ */

static char *
get_message(struct msgMap *msg, int id)
{
    int i;

    for (i = 0; msg[i].message != NULL; i++) {
        if (msg[i].number == id) {
            return ((char *)msg[i].message);
        }
    }

    return EMPTY_STRING;
}

static void
get_userindex_message(unsigned int index, int reason,
                      struct resource_definition *restable)
{
    sstrIndexMsg[0] = '\0';

    if (NULL == restable || index <= MEM) {
        snprintf(sstrIndexMsg, MAXMESSAGELENGTH, "Load index is beyond threshold");
        return;
    }

    if (reason == REASON_NODE_LOAD) {
        snprintf(sstrIndexMsg, MAXMESSAGELENGTH,
                 "Load index (%s) is beyond threshold", restable[index].name);
    } else if (reason == REASON_QUEUE_RUSAGE) {
        snprintf(sstrIndexMsg, MAXMESSAGELENGTH,
                 "Queue requirements for reserving resource (%s) not satisfied",
                 restable[index].name);
    } else if (reason == REASON_SUBMIT_RUSAGE) {
        snprintf(sstrIndexMsg, MAXMESSAGELENGTH,
                 "Job's requirements for reserving resource (%s) not satisfied",
                 restable[index].name);
    }

    return;
}

static void
get_resource_message(int resource, int reason, struct resource_definition *restable)
{
    sstrIndexMsg[0] = '\0';

    switch (resource) {
    case R1M:

        if (reason == REASON_NODE_LOAD) {
            snprintf(sstrIndexMsg, MAXMESSAGELENGTH,
                     "The 1 min effective CPU queue length (r1m) is beyond threshold");
        } else if (reason == REASON_QUEUE_RUSAGE) {
            snprintf(sstrIndexMsg, MAXMESSAGELENGTH,
                     "Queue requirements for reserving resource (r1m) not satisfied ");
        } else if (reason == REASON_SUBMIT_RUSAGE) {
            snprintf(sstrIndexMsg, MAXMESSAGELENGTH,
                     "Job requirements for reserving resource (r1m) not satisfied");
        }

        break;
    case R5M:

        if (reason == REASON_NODE_LOAD) {
            snprintf(sstrIndexMsg, MAXMESSAGELENGTH,
                     "The 5 min effective CPU queue length (r5m) is beyond threshold");
        } else if (reason == REASON_QUEUE_RUSAGE) {
            snprintf(sstrIndexMsg, MAXMESSAGELENGTH,
                     "Queue requirements for reserving resource (r5m) not satisfied ");
        } else if (reason == REASON_SUBMIT_RUSAGE) {
            snprintf(sstrIndexMsg, MAXMESSAGELENGTH,
                     "Job requirements for reserving resource (r5m) not satisfied");
        }

        break;
    case R15M:

        if (reason == REASON_NODE_LOAD) {
            snprintf(sstrIndexMsg, MAXMESSAGELENGTH,
                     "The 15 min effective CPU queue length (r15m) is beyond threshold");
        } else if (reason == REASON_QUEUE_RUSAGE) {
            snprintf(sstrIndexMsg, MAXMESSAGELENGTH,
                     "Queue requirements for reserving resource (r15m) not satisfied ");
        } else if (reason == REASON_SUBMIT_RUSAGE) {
            snprintf(sstrIndexMsg, MAXMESSAGELENGTH,
                     "Job requirements for reserving resource (r15m) not satisfied");
        }

        break;
    case UT:

        if (reason == REASON_NODE_LOAD) {
            snprintf(sstrIndexMsg, MAXMESSAGELENGTH,
                     "The CPU utilization (ut) is beyond threshold");
        } else if (reason == REASON_QUEUE_RUSAGE) {
            snprintf(sstrIndexMsg, MAXMESSAGELENGTH,
                     "Queue requirements for reserving resource (ut) not satisfied ");
        } else if (reason == REASON_SUBMIT_RUSAGE) {
            snprintf(sstrIndexMsg, MAXMESSAGELENGTH,
                     "Job requirements for reserving resource (ut) not satisfied");
        }

        break;
    case PG:

        if (reason == REASON_NODE_LOAD) {
            snprintf(sstrIndexMsg, MAXMESSAGELENGTH,
                     "The paging rate (pg) is beyond threshold");
        } else if (reason == REASON_QUEUE_RUSAGE) {
            snprintf(sstrIndexMsg, MAXMESSAGELENGTH,
                     "Queue requirements for reserving resource (pg) not satisfied");
        } else if (reason == REASON_SUBMIT_RUSAGE) {
            snprintf(sstrIndexMsg, MAXMESSAGELENGTH,
                     "Job requirements for reserving resource (pg) not satisfied");
        }

        break;
    case IO:

        if (reason == REASON_NODE_LOAD) {
            snprintf(sstrIndexMsg, MAXMESSAGELENGTH,
                     "The disk IO rate (io) is beyond threshold");
        } else if (reason == REASON_QUEUE_RUSAGE) {
            snprintf(sstrIndexMsg, MAXMESSAGELENGTH,
                     "Queue requirements for reserving resource (io) not satisfied");
        } else if (reason == REASON_SUBMIT_RUSAGE) {
            snprintf(sstrIndexMsg, MAXMESSAGELENGTH,
                     "Job requirements for reserving resource (io) not satisfied");
        }

        break;
    case TMP:

        if (reason == REASON_NODE_LOAD) {
            snprintf(sstrIndexMsg, MAXMESSAGELENGTH, "The available /tmp space (tmp) is low");
        } else if (reason == REASON_QUEUE_RUSAGE) {
            snprintf(sstrIndexMsg, MAXMESSAGELENGTH,
                     "Queue requirements for reserving resource (tmp) not satisfied");
        } else if (reason == REASON_SUBMIT_RUSAGE) {
            snprintf(sstrIndexMsg, MAXMESSAGELENGTH,
                     "Job requirements for reserving resource (tmp) not satisfied");
        }

        break;
    case SWP:

        if (reason == REASON_NODE_LOAD) {
            snprintf(sstrIndexMsg, MAXMESSAGELENGTH, "The available swap space (swp) is low");
        } else if (reason == REASON_QUEUE_RUSAGE) {
            snprintf(sstrIndexMsg, MAXMESSAGELENGTH,
                     "Queue requirements for reserving resource (swp) not satisfied");
        } else if (reason == REASON_SUBMIT_RUSAGE) {
            snprintf(sstrIndexMsg, MAXMESSAGELENGTH,
                     "Job requirements for reserving resource (swp) not satisfied");
        }

        break;
    case MEM:

        if (reason == REASON_NODE_LOAD) {
            snprintf(sstrIndexMsg, MAXMESSAGELENGTH, "The available memory (mem) is low");
        } else if (reason == REASON_QUEUE_RUSAGE) {
            snprintf(sstrIndexMsg, MAXMESSAGELENGTH,
                     "Queue requirements for reserving resource (mem) not satisfied");
        } else if (reason == REASON_SUBMIT_RUSAGE) {
            snprintf(sstrIndexMsg, MAXMESSAGELENGTH,
                     "Job requirements for reserving resource (mem) not satisfied");
        }

        break;
    default:

        get_userindex_message(resource, reason, restable);
        break;
    }

    return;
} // end function get_resource_message
